const fs = require('fs')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const path = require('path')
const {transformFromAst} = require('@babel/core')

module.exports = class Webpack{ 
	constructor(options) {
	    // console.log(options);
		const {entry, output} = options;
		this.entry = entry
		this.output = output
		
		this.modules = []
	}
	run() {
		const info = this.parse(this.entry)
		// console.log(info);
		
		// 处理其他模块，然后汇总
		this.modules.push(info)
		for(let i = 0; i < this.modules.length; i++) {
			const item = this.modules[i]
			const {dependencies} = item
			if(dependencies) {
				for(let j in dependencies) {
					this.modules.push(this.parse(dependencies[j]))
				}
			}
		}
		// console.log(this.modules);
		// 数组=>对象
		const obj = {}
		this.modules.forEach((item) => {
			obj[item.entryFile] = {
				dependencies: item.dependencies,
				code: item.code
			}
		})
		// console.log(obj);
		this.file(obj)
	}
	parse(entryFile) {
		// 分析入口文件
		const content = fs.readFileSync(entryFile, 'utf-8')
		// console.log(content);
		
		// 分析依赖及以来的路径
		// 使用parser把内容抽象成语法树
		const ast = parser.parse(content, {
			sourceType: 'module'
		})
		
		const dependencies = {}
		traverse(ast, {
			ImportDeclaration({node}) {
				const finalName = './' + path.join(path.dirname(entryFile), node.source.value)
				// console.log(finalName);
				dependencies[node.source.value] = finalName
			}
		})
		// console.log(dependencies);
		// 处理内容，转换ast
		const {code} = transformFromAst(ast, null, {
			presets: ['@babel/preset-env']
		})
		// console.log(code);
		return {
			entryFile,
			dependencies,
			code
		}
	}
	file(code) {
		// 生成文件
		const filePath = path.join(this.output.path, this.output.filename)
		const newCode = JSON.stringify(code)
		const bundle = `(function(graph) {
			function require(module){
				function localRequire(relativePath) {
					return require(graph[module].dependencies[relativePath])
				}
				var exports = {};
				(function(require, exports, code) {
					eval(code)
				})(localRequire, exports, graph[module].code)
				
				return exports
			}
			require('${this.entry}')
		})(${newCode})`
		
		fs.writeFileSync(filePath, bundle, 'utf-8')
	}
}